/*
 * Copyright 2008-2023 dexian.vip. All rights reserved.
 * Support: http://www.dexian.vip
 * License: http://www.dexian.vip/license
 */

package vip.dexian.core.base.dao;

import cn.hutool.core.lang.Assert;
import vip.dexian.common.query.BaseBetween;
import vip.dexian.common.query.DecimalBetween;
import vip.dexian.common.query.Filter;
import vip.dexian.common.query.Listable;
import vip.dexian.common.query.LocalDateTimeBetween;
import vip.dexian.common.query.LongBetween;
import vip.dexian.common.query.Operator;
import vip.dexian.common.query.Order;
import vip.dexian.common.query.Page;
import vip.dexian.common.query.Pageable;
import vip.dexian.core.base.bean.BaseEntity;
import vip.dexian.core.base.bean.OrderEntity;
import vip.dexian.core.base.bean.UniqueOrderEntity;
import jakarta.persistence.EntityManager;
import jakarta.persistence.NoResultException;
import jakarta.persistence.PersistenceContext;
import jakarta.persistence.TypedQuery;
import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.CriteriaQuery;
import jakarta.persistence.criteria.Expression;
import jakarta.persistence.criteria.Fetch;
import jakarta.persistence.criteria.From;
import jakarta.persistence.criteria.Join;
import jakarta.persistence.criteria.Path;
import jakarta.persistence.criteria.Predicate;
import jakarta.persistence.criteria.Root;
import jakarta.persistence.criteria.Selection;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.core.ResolvableType;

import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 该类主要放置一些公共方法和CriteriaQuery查询，减少BaseDaoImpl的代码量
 *
 * @author 挺好的 2023年05月30日 18:41
 */
@Slf4j
public abstract class BaseCriteriaQueryDaoImpl<T extends BaseEntity <ID>, ID extends Serializable> {


    /**
     * 属性分隔符
     */
    private static final String PROPERTY_SEPARATOR = ".";

    /**
     * 别名数量
     */
    private static volatile long aliasCount = 0L;

    /**
     * 别名前缀
     */
    private static final String ALIAS_PREFIX = "generatedAlias";

    /**
     * 实体类Class对象
     */
    protected Class <T> entityClass;

    /**
     * jpa 实体类管理器
     */
    @PersistenceContext
    protected EntityManager entityManager;

    /**
     * 从泛型中获取entityClass
     */
    @SuppressWarnings ("unchecked")
    public BaseCriteriaQueryDaoImpl () {
        ResolvableType resolvableType = ResolvableType.forClass(this.getClass());
        this.entityClass = (Class <T>) resolvableType.getSuperType().getGeneric().resolve();
    }

    /**
     * 创建通用的CriteriaQuery，该方法仅适用于findList，count, page方法。
     * 如果需要更高级的查询，如：表连接等操作，请重新创建CriteriaQuery
     *
     * @return 顶级查询接口，通俗的讲就是：查询哪些字段，排序是什么
     */
    protected CriteriaQuery <T> createUniversalQuery () {
        CriteriaBuilder criteriaBuilder = this.entityManager.getCriteriaBuilder();

        // 查询结果所需要的类型，与Entity对应
        CriteriaQuery <T> criteriaQuery = criteriaBuilder.createQuery(this.entityClass);

        // 查询结果 - select ，此处查询所有符合条件的主题类。 from表示具体查询哪张表
        criteriaQuery.select(criteriaQuery.from(this.entityClass));

        return criteriaQuery;
    }

    /**
     * 校验 CriteriaQuery 对象是否为空 | Selection(查询结果返回的项目)是否为空 | 查询的根不能为空
     *
     * @param criteriaQuery
     *         顶级查询接口，通俗的讲就是：查询哪些字段，排序是什么
     */
    protected void assertCriteriaQuery (CriteriaQuery <T> criteriaQuery) {
        Assert.notNull(criteriaQuery, "criteriaQuery 对象不能为空");
        Assert.notNull(criteriaQuery.getSelection(), "Selection(查询结果返回的项目)不能为空");
        Assert.notEmpty(criteriaQuery.getRoots(), "查询的根不能为空");
    }

    /**
     * 查找列表
     *
     * @param criteriaQuery
     *         顶级查询接口，通俗的讲就是：查询哪些字段，排序是什么
     * @param listable
     *         列表搜索条件
     *
     * @return 搜索到的结果
     *
     * @see Listable
     */
    protected List <T> findList (CriteriaQuery <T> criteriaQuery, Listable listable) {

        if (listable == null) {
            listable = new Listable();
        }

        this.assertCriteriaQuery(criteriaQuery);

        // 添加搜索条件并且获取查询根
        this.addPredicateAndOrder(criteriaQuery, listable.getFilters(), listable.getOrders(), listable.getGroups(),
                true, listable.getIsEnableDefaultOrder()
        );

        TypedQuery <T> query = this.entityManager.createQuery(criteriaQuery);

        Integer first = listable.resetFirst();
        Integer count = listable.resetCount();

        if (first != null) {
            query.setFirstResult(first);
        }
        if (count != null) {
            query.setMaxResults(count);
        }

        return query.getResultList();
    }

    /**
     * 根据搜索条件查找单个对象
     *
     * @param criteriaQuery
     *         顶级查询接口，通俗的讲就是：查询哪些字段，排序是什么
     * @param filters
     *         搜索条件
     *
     * @return T
     */
    protected T find (CriteriaQuery <T> criteriaQuery, List <Filter> filters) {
        this.assertCriteriaQuery(criteriaQuery);
        // 添加搜索条件并且获取查询根
        this.addPredicateAndOrder(criteriaQuery, filters, null, null, false, false);

        TypedQuery <T> query = this.entityManager.createQuery(criteriaQuery);

        try {
            return query.getSingleResult();
        } catch (NoResultException e) {
            return null;
        }
    }


    /**
     * 统计当前有多少数据
     *
     * @param criteriaQuery
     *         查询接口
     * @param filters
     *         查询条件
     *
     * @return 数据数量
     */
    protected Long count (CriteriaQuery <T> criteriaQuery, List <Filter> filters) {

        this.assertCriteriaQuery(criteriaQuery);

        // 创建查询工厂
        CriteriaBuilder criteriaBuilder = this.entityManager.getCriteriaBuilder();

        CriteriaQuery <Long> countCriteriaQuery = criteriaBuilder.createQuery(Long.class);

        Set <Root <?>> roots = criteriaQuery.getRoots();

        Root <T> countRoot;
        // TODO 当有多个Root的时候，别名问题未测试，hibernate6 和 5差别很大
        if (roots.size() > 1) {
            for (Root <?> r : criteriaQuery.getRoots()) {
                Root <?> dest = countCriteriaQuery.from(r.getJavaType());
                dest.alias(this.getAlias(r));
                this.copyJoins(r, dest);
            }

            countRoot = this.getRoot(countCriteriaQuery, criteriaQuery.getResultType());
        } else {
            countRoot = countCriteriaQuery.from(this.entityClass);
        }

        // 是否去重复
        if (criteriaQuery.isDistinct()) {
            countCriteriaQuery.select(criteriaBuilder.countDistinct(countRoot));
        } else {
            countCriteriaQuery.select(criteriaBuilder.count(countRoot));
        }

        Predicate predicate = this.toPredicate(countRoot, filters);

        // 设置搜索条件
        if (predicate != null) {
            countCriteriaQuery.where(predicate);
        }

        return this.entityManager.createQuery(countCriteriaQuery).getSingleResult();
    }


    /**
     * 分页查找数据
     *
     * @param criteriaQuery
     *         顶级查询接口，通俗的讲就是：查询哪些字段，排序是什么
     * @param pageable
     *         分页条件
     *
     * @return 分页对象
     */
    protected Page <T> findPage (CriteriaQuery <T> criteriaQuery, Pageable pageable) {

        this.assertCriteriaQuery(criteriaQuery);

        if (pageable == null) {
            pageable = new Pageable();
        }

        List <Filter> filters = pageable.getFilters();
        List <Order> orders = pageable.getOrders();
        List <String> groups = pageable.getGroups();

        this.addPredicateAndOrder(criteriaQuery, filters, orders, groups, true, pageable.getIsEnableDefaultOrder());

        long total = 0L;

        if (pageable.isCount()) {
            total = this.count(criteriaQuery, filters);
        }

        TypedQuery <T> query = this.entityManager.createQuery(criteriaQuery);
        query.setFirstResult(pageable.getStartPosition());
        query.setMaxResults(pageable.getPageSize());

        return new Page <>(query.getResultList(), total, pageable);
    }

    /**
     * 添加查询条件+排序条件。
     * <p>
     * 1. 校验 criteriaQuery 不为空
     * 2. 是否指定了查询结果中的返回项目。通过criteriaQuery.select(Selection)指定
     * 3. 是否添加了给定实体对应的查询根。通过criteriaQuery.from(Class)指定
     *
     * @param criteriaQuery
     *         顶级查询接口。不可为空
     * @param filters
     *         搜索条件
     * @param orders
     *         排序条件
     * @param groupBys
     *         group by 条件
     * @param isOrder
     *         是否排序。如果是否，则不进行orderBy操作，通常count的时候不需要
     * @param isEnableDefaultOrder
     *         是否启用默认排序
     */
    protected void addPredicateAndOrder (CriteriaQuery <T> criteriaQuery, List <Filter> filters, List <Order> orders,
            List <String> groupBys, boolean isOrder, Boolean isEnableDefaultOrder) {

        // 创建查询工厂
        CriteriaBuilder criteriaBuilder = this.entityManager.getCriteriaBuilder();
        Root <T> root = this.getRoot(criteriaQuery);

        // 如果criteriaQuery中没有查询条件，则创建一个逻辑与(AND)的条件，最终表现在sql上为1=1
        // 1=1在最终的查询中并不会影响效率，可以通过MYSQL SHOW WARNINGS; 查看SQL优化器生成的SQL语句
        Predicate restrictions = criteriaQuery.getRestriction() != null ? criteriaQuery.getRestriction() : criteriaBuilder.conjunction();
        //        ImmutableCollection


        // 其他查询条件
        Predicate predicate = this.toPredicate(root, filters);
        // 有其他的查询条件，则添加到当前条件中
        if (predicate != null) {
            restrictions = criteriaBuilder.and(restrictions, predicate);
        }

        criteriaQuery.where(restrictions);

        // 添加group by
        this.addGroupBy(root, criteriaQuery, groupBys);

        if (!isOrder) {
            return;
        }

        List <jakarta.persistence.criteria.Order> orderList = new ArrayList <>();
        orderList.addAll(criteriaQuery.getOrderList());
        orderList.addAll(this.toOrders(root, orders));

        // 如果没有添加排序条件，如果实体类继承自OrderEntity/UniqueOrderEntity，按排序升序。否则按照ID降序
        if (orderList.isEmpty() && BooleanUtils.isTrue(isEnableDefaultOrder)) {
            if (OrderEntity.class.isAssignableFrom(this.entityClass) || UniqueOrderEntity.class.isAssignableFrom(
                    this.entityClass)) {
                orderList.add(criteriaBuilder.asc(this.getPath(root, OrderEntity.ORDER_PROPERTY_NAME)));
            } else {
                orderList.add(criteriaBuilder.desc(this.getPath(root, BaseEntity.ID_PROPERTY_NAME)));
            }
        }
        criteriaQuery.orderBy(orderList);
    }


    /**
     * 返回与根对应的实体模型，对应的是实体类。通俗的讲就是要查询哪张表，相当于FROM子句
     *
     * @param criteriaQuery
     *         顶级查询接口，通俗的讲就是：查询哪些字段，排序是什么
     *
     * @return 对应于根的元模型实体
     */
    protected Root <T> getRoot (CriteriaQuery <T> criteriaQuery) {
        if (criteriaQuery == null) {
            return null;
        }
        return this.getRoot(criteriaQuery, criteriaQuery.getResultType());
    }

    /**
     * 返回与根对应的实体模型，对应的是实体类。通俗的讲就是要查询哪张表，相当于FROM子句
     *
     * @param criteriaQuery
     *         顶级查询接口，通俗的讲就是：查询哪些字段，排序是什么
     * @param clazz
     *         实体类对应的class
     *
     * @return 对应于根的元模型实体
     */
    protected Root <T> getRoot (CriteriaQuery <?> criteriaQuery, Class <T> clazz) {
        if (criteriaQuery == null || CollectionUtils.isEmpty(criteriaQuery.getRoots()) || clazz == null) {
            return null;
        }

        for (Root <?> root : criteriaQuery.getRoots()) {
            if (clazz.equals(root.getJavaType())) {
                return (Root <T>) root;
            }
        }
        return null;
    }

    /**
     * 获取 Root(表示某个具体的实体类)中某个属性的路径
     *
     * @param path
     *         表示绑定类型或集合中的简单或复合属性路径。通常是Root
     * @param propertyPath
     *         属性名称
     * @param <X>
     *         对应的具体类型
     *
     * @return 属性的具体路径信息（包含属性名称、类型、别名等信息）
     */
    @SuppressWarnings ("unchecked")
    protected <X> Path <X> getPath (Path <?> path, String propertyPath) {
        if (path == null || StringUtils.isEmpty(propertyPath)) {
            return (Path <X>) path;
        }
        String property = StringUtils.substringBefore(propertyPath, PROPERTY_SEPARATOR);
        return this.getPath(path.get(property), StringUtils.substringAfter(propertyPath, PROPERTY_SEPARATOR));
    }

    /**
     * 创建查询条件。
     *
     * @param root
     *         对应于根的元模型实体
     * @param filters
     *         过滤条件
     *
     * @return 查询条件的组合，AND 1=1、OR  name LIKE '%name%'等
     */
    protected Predicate toPredicate (Root <T> root, List <Filter> filters) {

        if (root == null || CollectionUtils.isEmpty(filters)) {
            return null;
        }

        // 创建一个查询表示工厂，用于表示字段之间是什么关系，如何生成一个查询条件，每一个查询条件都是什么方式
        CriteriaBuilder criteriaBuilder = this.entityManager.getCriteriaBuilder();

        // 创建一个逻辑与条件( AND )，SQL表现为 1=1
        Predicate restrictions = criteriaBuilder.conjunction();

        // 添加搜索条件
        for (Filter filter : filters) {

            if (filter == null || !filter.isValid()) {
                continue;
            }

            Object value = filter.getValue();

            String property = filter.getProperty();

            Path <?> path = this.getPath(root, property);

            if (path == null) {
                continue;
            }

            Operator operator = filter.getOperator();

            boolean ignoreCase = filter.isIgnoreCase();

            switch (operator) {
                case EQ -> {
                    if (BooleanUtils.isTrue(ignoreCase) && this.isString(path, value)) {
                        restrictions = criteriaBuilder.and(restrictions,
                                criteriaBuilder.equal(criteriaBuilder.lower((Path <String>) path),
                                        ((String) value).toLowerCase()
                                )
                        );
                    } else {
                        restrictions = criteriaBuilder.and(restrictions, criteriaBuilder.equal(path, value));
                    }
                }
                case NE -> {
                    if (BooleanUtils.isTrue(ignoreCase) && this.isString(path, value)) {
                        restrictions = criteriaBuilder.and(restrictions,
                                criteriaBuilder.notEqual(criteriaBuilder.lower((Path <String>) path),
                                        ((String) value).toLowerCase()
                                )
                        );
                    } else {
                        restrictions = criteriaBuilder.and(restrictions, criteriaBuilder.notEqual(path, value));
                    }
                }
                case GT -> {
                    if (this.isNumber(path, value)) {
                        restrictions = criteriaBuilder.and(restrictions,
                                criteriaBuilder.gt((Path <Number>) path, (Number) value)
                        );
                    }
                }
                case LT -> {
                    if (this.isNumber(path, value)) {
                        restrictions = criteriaBuilder.and(restrictions,
                                criteriaBuilder.lt((Path <Number>) path, (Number) value)
                        );
                    }
                }
                case GE -> {
                    if (this.isNumber(path, value)) {
                        restrictions = criteriaBuilder.and(restrictions,
                                criteriaBuilder.ge((Path <Number>) path, (Number) value)
                        );
                    }
                }
                case LE -> {
                    if (this.isNumber(path, value)) {
                        restrictions = criteriaBuilder.and(restrictions,
                                criteriaBuilder.le((Path <Number>) path, (Number) value)
                        );
                    }
                }
                case LIKE -> {
                    if (!this.isString(path, value)) {
                        break;
                    }
                    String likeValue = "%" + value + "%";
                    if (BooleanUtils.isTrue(ignoreCase)) {
                        restrictions = criteriaBuilder.and(restrictions,
                                criteriaBuilder.like(criteriaBuilder.lower((Path <String>) path),
                                        (likeValue).toLowerCase()
                                )
                        );
                    } else {
                        restrictions = criteriaBuilder.and(restrictions,
                                criteriaBuilder.like((Path <String>) path, likeValue)
                        );
                    }
                }
                case RIGHT_LIKE -> {
                    if (!this.isString(path, value)) {
                        break;
                    }
                    String likeRightValue = value + "%";
                    if (BooleanUtils.isTrue(ignoreCase)) {
                        restrictions = criteriaBuilder.and(restrictions,
                                criteriaBuilder.like(criteriaBuilder.lower((Path <String>) path),
                                        (likeRightValue).toLowerCase()
                                )
                        );
                    } else {
                        restrictions = criteriaBuilder.and(restrictions,
                                criteriaBuilder.like((Path <String>) path, likeRightValue)
                        );
                    }
                }
                case IN -> {
                    // 是否包含 “.”，
                    if (property.contains(PROPERTY_SEPARATOR)) {
                        restrictions = criteriaBuilder.and(restrictions, path.in(value));
                    } else {
                        restrictions = criteriaBuilder.and(restrictions,
                                criteriaBuilder.in(root.get(property)).value(value)
                        );
                    }
                }
                case BETWEEN -> {
                    if (!this.isBetween(value)) {
                        break;
                    }
                    restrictions = this.addBetween(value, path, restrictions, criteriaBuilder);
                }
                case IS_TRUE -> {
                    if (!this.isBoolean(path, value)) {
                        break;
                    }
                    restrictions = criteriaBuilder.and(restrictions, criteriaBuilder.isTrue((Path <Boolean>) path));
                }
                case IS_FALSE -> {
                    if (!this.isBoolean(path, value)) {
                        break;
                    }
                    restrictions = criteriaBuilder.and(restrictions, criteriaBuilder.isFalse((Path <Boolean>) path));
                }
                case IS_NULL -> restrictions = criteriaBuilder.and(restrictions, path.isNull());
                case IS_NOT_NULL -> restrictions = criteriaBuilder.and(restrictions, path.isNotNull());
                case OR -> {
                    if (!this.isOr(value)) {
                        break;
                    }
                    restrictions = this.addOr(value, path, restrictions, criteriaBuilder);
                }
                default -> {
                }
            }
        }
        return restrictions;
    }

    /**
     * 获取group by
     *
     * @param root
     * @param criteriaQuery
     * @param groups
     *         分组的属性名称
     */
    protected void addGroupBy (Root <T> root, CriteriaQuery <T> criteriaQuery, List <String> groups) {

        if (CollectionUtils.isEmpty(groups)) {
            return;
        }

        List <Expression <?>> grouping = groups.stream().map(o -> this.getPath(root, o)).collect(Collectors.toList());

        criteriaQuery.groupBy(grouping);
    }

    /**
     * 获取别名
     *
     * @param selection
     *         Selection接口定义要在查询结果中返回的项目
     *
     * @return 表别名
     */
    protected synchronized String getAlias (Selection <?> selection) {
        if (selection == null) {
            return null;
        }
        String alias = selection.getAlias();
        if (alias == null) {
            if (aliasCount >= 1000) {
                aliasCount = 0;
            }
            alias = ALIAS_PREFIX + aliasCount++;
            selection.alias(alias);
        }
        return alias;
    }

    /**
     * 复制Join 及 Fetch
     *
     * @param from
     *         源类型
     * @param to
     *         目标类型
     */
    private void copyJoins (From <?, ?> from, From <?, ?> to) {
        for (Join <?, ?> join : from.getJoins()) {
            Join <?, ?> toJoin = to.join(join.getAttribute().getName(), join.getJoinType());
            toJoin.alias(this.getAlias(join));
            this.copyJoins(join, toJoin);
        }
        for (Fetch <?, ?> fetch : from.getFetches()) {
            Fetch <?, ?> toFetch = to.fetch(fetch.getAttribute().getName());
            this.copyFetches(fetch, toFetch);
        }
    }

    /**
     * 复制fetch
     *
     * @param from
     *         源类型
     * @param to
     *         目标类型
     */
    private void copyFetches (Fetch <?, ?> from, Fetch <?, ?> to) {
        for (Fetch <?, ?> fetch : from.getFetches()) {
            Fetch <?, ?> toFetch = to.fetch(fetch.getAttribute().getName());
            this.copyFetches(fetch, toFetch);
        }
    }

    /**
     * 是否是范围查询
     *
     * @param value
     *         值
     *
     * @return 如果是Between或者子类，返回true
     */
    protected boolean isBetween (Object value) {
        return value instanceof BaseBetween;
    }

    /**
     * 是否是字符串
     *
     * @param path
     *         属性路径
     * @param value
     *         值
     *
     * @return 必须为 String.class 并且 value instanceof String 才返回true.
     */
    protected boolean isString (Path <?> path, Object value) {
        return String.class.isAssignableFrom(path.getJavaType()) && value instanceof String;
    }

    /**
     * 是否是数字
     *
     * @param path
     *         属性路径
     * @param value
     *         值
     *
     * @return 必须为 Number.class 并且 value instanceof Number 才返回true.
     */
    protected boolean isNumber (Path <?> path, Object value) {
        return Number.class.isAssignableFrom(path.getJavaType()) && value instanceof Number;
    }

    /**
     * 是否是localDateTime
     *
     * @param path
     *         属性路径
     * @param value
     *         值
     *
     * @return 必须为LocalDateTime.class 并且 value instanceof LocalDateTime才返回true
     */
    protected boolean isLocalDateTime (Path <?> path, Object value) {
        return LocalDateTime.class.isAssignableFrom(path.getJavaType()) && value instanceof LocalDateTime;
    }

    /**
     * 是否是布尔值
     *
     * @param path
     *         属性路径
     *
     * @return 必须为 Boolean.class && value instanceof Boolean， 才返回true.
     */
    protected boolean isBoolean (Path <?> path, Object value) {
        return Boolean.class.isAssignableFrom(path.getJavaType()) && (value instanceof Boolean);
    }

    /**
     * 是否是or的条件
     *
     * @param value
     *         值{@link Filter}
     *
     * @return
     */
    protected boolean isOr (Object value) {
        return ObjectUtils.isNotEmpty(value);
    }

    /**
     * 设置排序条件
     *
     * @param root
     *         通俗的讲就是要查询哪张表，相当于FROM子句
     * @param orders
     *         排序条件
     *
     * @return 排序条件
     */
    protected List <jakarta.persistence.criteria.Order> toOrders (Root <T> root, List <Order> orders) {

        List <jakarta.persistence.criteria.Order> orderList = new ArrayList <>();
        if (root == null || CollectionUtils.isEmpty(orders)) {
            return orderList;
        }

        CriteriaBuilder criteriaBuilder = this.entityManager.getCriteriaBuilder();

        for (Order order : orders) {
            if (order == null || !order.isValid()) {
                continue;
            }

            String property = order.getProperty();
            Order.Direction direction = order.getDirection();
            Path <?> path = this.getPath(root, property);

            if (path == null) {
                continue;
            }

            switch (direction) {
                case ASC:
                    orderList.add(criteriaBuilder.asc(path));
                    break;
                case DESC:
                    orderList.add(criteriaBuilder.desc(path));
                    break;
                default:
            }
        }

        return orderList;
    }

    /**
     * 添加范围查询条件。如果没有，则返回原来的
     *
     * @param value
     *         值
     * @param path
     *         属性路径
     * @param restrictions
     *         搜索条件
     * @param criteriaBuilder
     *         查询工厂
     *
     * @return 拼接后的搜索条件
     */
    protected Predicate addBetween (Object value, Path <?> path, Predicate restrictions,
            CriteriaBuilder criteriaBuilder) {

        BaseBetween between = (BaseBetween) value;

        // 如果左右两边的值都为null，则不进行between操作
        if (!between.isValid()) {
            return restrictions;
        }

        if (value instanceof LocalDateTimeBetween localDateTimeBetween) {

            restrictions = criteriaBuilder.and(restrictions,
                    criteriaBuilder.between((Path <LocalDateTime>) path, localDateTimeBetween.getLowerBound(),
                            localDateTimeBetween.getUpperBound()
                    )
            );

        } else if (value instanceof LongBetween longBetween) {

            restrictions = criteriaBuilder.and(restrictions,
                    criteriaBuilder.between((Path <Long>) path, longBetween.getLowerBound(),
                            longBetween.getUpperBound()
                    )
            );
        } else if (value instanceof DecimalBetween decimalBetween) {

            restrictions = criteriaBuilder.and(restrictions,
                    criteriaBuilder.between((Path <BigDecimal>) path, decimalBetween.getLowerBound(),
                            decimalBetween.getUpperBound()
                    )
            );

        }

        return restrictions;
    }

    /**
     * 添加or条件
     *
     * @param value
     *         值
     * @param path
     *         属性路径
     * @param restrictions
     *         搜索条件
     * @param criteriaBuilder
     *         查询工厂
     *
     * @return 拼接后的搜索条件
     */
    protected Predicate addOr (Object value, Path <?> path, Predicate restrictions, CriteriaBuilder criteriaBuilder) {

        List <Filter> filters = (List <Filter>) value;

        List <Predicate> predicates = new ArrayList <>();

        // 如果条件多的，可以使用switch方式
        for (Filter filter : filters) {
            if (Operator.EQ.equals(filter.getOperator())) {
                predicates.add(criteriaBuilder.equal(path, filter.getValue()));
            }
        }

        return criteriaBuilder.and(restrictions, criteriaBuilder.or(predicates.toArray(new Predicate[] {})));
    }
}
